package com.nut.common;

import com.nut.util.JacksonUtils;
import org.springframework.stereotype.Component;

import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;


/**
 * 基于内存缓存，提供超时时间选项，依赖外部定时任务，清理过期对象
 */
@Component
public class CacheInMap {

    private final ConcurrentHashMap<String, Object> caches = new ConcurrentHashMap<>(64);

    public void setEnableCache(boolean enableCache) {
        this.enableCache = enableCache;
    }

    public boolean isEnableCache() {
        return enableCache;
    }

    //是否启用缓存
    private volatile boolean enableCache = true;

    /**
     * 带超时时间,惰性删除缓存
     *
     * @param key
     * @param supplier
     * @param amount
     * @param timeUnit
     * @return
     */
    public Object findCache(String key, Supplier supplier, int amount, TimeUnit timeUnit) {

        if (!enableCache) {
            return supplier.get();
        }

        Object t = caches.get(key);
        if (t != null) {
            if (t instanceof ExpireTimeCache) {
                ExpireTimeCache o = (ExpireTimeCache) t;
                if (o.isExpired()) {
                    caches.remove(key);
                } else {
                    return o.getCache();
                }
            } else {
                return t;
            }
        }

        Object load = null;
        synchronized (key.intern()) {
            System.out.println("do_supplier");
//            try {
//                TimeUnit.SECONDS.sleep(5);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
            load = supplier.get();
//            if (load instanceof Result) {
//                Result result = (Result) load;
//                if (result.getCode() != 0) {
//                    return result;
//                }
//            }

            ExpireTimeCache expireTimeCache = new ExpireTimeCache(load, amount, timeUnit);
            caches.put(key, expireTimeCache);
        }
        return load;
    }

    public Object findCache(String key, Supplier supplier) {

        if (!enableCache) {
            return supplier.get();
        }

        Object t = caches.get(key);
        if (t != null) {
            return t;
        }

        Object load = null;
        synchronized (key.intern()) {
            load = supplier.get();
            caches.put(key, load);
        }
        return load;
    }

    /**
     * 依赖外部定时任务  清理过期对象
     */
    public void ClearExpiredObject() {

        Enumeration<String> keys = caches.keys();
        while (keys.hasMoreElements()) {
            String s = keys.nextElement();
            Object o = caches.get(s);
            if (o instanceof ExpireTimeCache) {
                ExpireTimeCache a = (ExpireTimeCache) o;
                if (a.isExpired()) {
                    caches.remove(s);
                }
            }
        }
    }

    public int getSize() {
        return caches.size();
    }

    public void removeKey(String key) {
        caches.remove(key);
    }

    public String getKeys() {
        Enumeration<String> keys = caches.keys();

        StringBuilder sb = new StringBuilder();
        while (keys.hasMoreElements()) {
            String ele = keys.nextElement();
            sb.append("\r\n ");
            sb.append(ele);
        }
        return sb.toString();
    }

    public String getValue(String key) {
        Object obj = caches.get(key);
        return JacksonUtils.toJson(obj);
    }

    public void cleanAll() {
        caches.clear();
    }

    public class ExpireTimeCache {

        private long expireTime;
        private Object cache;

        public ExpireTimeCache(Object obj, int amount, TimeUnit timeUnit) {
            this.cache = obj;
            this.expireTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(amount, timeUnit);
        }

        public boolean isExpired() {
            return this.expireTime > System.currentTimeMillis() ? false : true;
        }

        public Object getCache() {
            return this.cache;
        }
    }

    public static void main(String[] args) throws Exception {

        System.out.println(System.currentTimeMillis());
        TimeUnit.SECONDS.sleep(3);
        System.out.println(System.currentTimeMillis());

        System.out.println(TimeUnit.MILLISECONDS.convert(3, TimeUnit.SECONDS));
        System.out.println(TimeUnit.HOURS.convert(3, TimeUnit.DAYS));
    }

}
